Skip to content

Fix live-converge word casing: stuck all-caps (#4) + dropped auto-cap (#5)#6

Merged
SHAWNERZZ merged 1 commit into
mainfrom
fix/live-converge-word-casing
Jun 9, 2026
Merged

Fix live-converge word casing: stuck all-caps (#4) + dropped auto-cap (#5)#6
SHAWNERZZ merged 1 commit into
mainfrom
fix/live-converge-word-casing

Conversation

@SHAWNERZZ

Copy link
Copy Markdown
Owner

Fixes two casing bugs specific to the live-converge (#1.7) merged-trail re-recognition. Both are gated to that path, so a plain single gesture — including an intentional standalone acronym swipe — is untouched.

#4 — word gets stuck in all-caps

A short, ambiguous swipe can make the glide recognizer's top pick an all-caps dictionary acronym (e.g. CSA), while the keyboard is unshifted. That sets WordComposer.isAllUpperCase, which makes Suggest.getSuggestedWordsForBatchInput force every later candidate to all-caps; live-converge then keeps replacing the word with the uppercased result, so it stays stuck (CSA → CAN → CAME, suggestions shown in caps) until the word is committed. Confirmed from a debug log — there is no setShiftLocked=true, so it isn't a caps-lock/Shift-key bug.

#5 — extending a word drops the auto-capital

The first-letter capital comes from mShiftModeAtGestureStart, captured at gesture start and cleared after the first commit. A live-converge extending tap re-recognizes the whole word and re-commits with that value already OFF, so Hello becomes hellow.

Fix

In InputLogic.onUpdateTailBatchInputCompleted, when usedMergedTrail is set, re-derive the case from the word being re-recognized:

Trade-off: gliding an acronym and then extending it normalizes to lowercase. A standalone acronym swipe (no extension) is unaffected.

Testing

  • :app:compileStandardDebugJavaWithJavac clean on this base (off main).
  • InputLogicTest.extendBase* (live-converge/merged-trail regression) green on the source branch.

Fixes #4
Fixes #5

Casing bugs specific to two-thumb live-converge / multi-part re-recognition,
where every extending tap or swipe REPLACES the whole composing word with the
recognizer's fresh (lowercase) output:

- Dropped auto-cap (#5): "Hello" -> "hellow" on the first extension.
- Stuck all-caps (#4): a short ambiguous swipe whose top pick is an all-caps
  acronym ("CSA") set WordComposer.isAllUpperCase, which forced every later
  suggestion upper, so the word stuck in caps ("CSA"->"CAN"->"CAME").
- Swipe-extension downcasing: "Was"+swipe -> "wait" (a second swipe re-entered
  onStartBatchInput and re-captured the now-cleared shift state).

Approach: separate a word's casing INTENT from the recognizer's letters.

- WordComposer.mCapitalizedMode is the persistent per-word intent: seeded at
  word start from auto-cap + shift, it survives the setBatchInputWord rebuild
  and is cleared only at commitWord. Exposed via getCapitalizedMode().
- New InputLogic.applyComposingCase(lemma, capsMode, locale) treats the
  recognizer output as a casing-NEUTRAL lemma (lowercased first) and re-applies
  the intent. Lowercasing first dissolves #4 at the source: the composing word
  is never all-caps, so isAllUpperCase never arms.
- onStartBatchInput captures the intent only for a FRESH word (gated on
  !extendComposingWord), so an extending gesture preserves the first fragment's
  intent instead of re-capturing the auto-cleared shift state (#5 / "Wait").
- Fresh-word gesture capitalization is unchanged (still mShiftModeAtGestureStart,
  captured before any state mutates), so plain glide typing is byte-identical
  and a standalone acronym swipe ("CSA") still stays as-is.

Tests: applyComposingCase covered directly (pure, native-free) plus intent
lifecycle tests in InputLogicTest. :app:testOfflineDebugUnitTest green except
the 3 documented pre-existing failures.

Fixes #4, #5.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@SHAWNERZZ SHAWNERZZ force-pushed the fix/live-converge-word-casing branch from cc21c01 to b1729bd Compare June 9, 2026 02:00
@SHAWNERZZ

Copy link
Copy Markdown
Owner Author

Force-pushed a refined implementation (replaces the earlier prior-word heuristic).

Why the change: the prior-word approach mis-fired on a sentence-start acronym (Cancan) and regressed swipe-extension casing (Was+swipe→wait). It also coupled three separate concerns into one inferred value.

New approach: separate a word's casing intent from the recognizer's letters.

Unit-tested directly (applyComposingCase is pure) plus intent-lifecycle tests; suite green except the 3 documented pre-existing failures. Validated on-device.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant